package com.cysyz.mylib.excel.impl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.cysyz.mylib.excel.ICsvReader;
import com.cysyz.mylib.excel.SheetHeader;
import com.cysyz.mylib.excel.annotation.ExcelField;
import com.cysyz.mylib.excel.exception.ExcelException;
import com.cysyz.mylib.excel.validator.FileNameValidator;


public class CsvReaderImpl implements ICsvReader {
	
	/** 
	 * @Title: importFile
	 * @Author: 崔勇
	 * @Date 2014-4-23 下午4:03:11
	 * @param csvFilePath
	 * @param charsetName
	 * @param sheetHeaders
	 * @param fileNameValidator
	 * @param clazz
	 * @return
	 * @throws ExcelException
	 */
	@Override
	public <T> List<T> importFile(String csvFilePath, String charsetName, List<SheetHeader> sheetHeaders, FileNameValidator fileNameValidator, Class<T> clazz) throws ExcelException {
		
		List<T> pojoList = new ArrayList<T>();
		List<String> errorList = new ArrayList<String>();
		BufferedReader br = null;
		try {
			
			br = new BufferedReader(new InputStreamReader(new FileInputStream(csvFilePath), charsetName));

			validateFileName(fileNameValidator, csvFilePath);
			
			Map<Integer, Field> fieldsMap = matchEexcelColToField(clazz);
			
			// 读取直到最后一行
			String line = "";
			int lineNum = 1;
			
			boolean valueHasNoError = true;
			while ((line = br.readLine()) != null) {
				String[] oneRowCells = line.split(",");
				if (lineNum == 1 && sheetHeaders != null && !sheetHeaders.isEmpty()) {
					validateSheetHeader(oneRowCells, sheetHeaders);
					lineNum++  ;
					continue;
				}
				
				// 校验一行中的内容是否跟标题长度相符
				if (oneRowCells.length < sheetHeaders.size()) {
					if (valueHasNoError) {
						valueHasNoError = false;
					}
					
					//csv文件中如果某一行的数据长度小于期望的列长度(由sheetHeaders的长度决定)
					errorList.add("第"+lineNum+"行的数据个数与期望的个数不相符：此行数据个数【"+oneRowCells.length+"】，期望的数据个数【"+sheetHeaders.size()+"】");
					lineNum++ ;
					continue;
				}
				
				// 一行中内容的校验
				for (int colNum = 0 ; colNum < sheetHeaders.size() ; colNum ++) {
					String errorMessage = sheetHeaders.get(colNum).validateCell(oneRowCells[colNum], lineNum, colNum);
					if (errorMessage != null) {
						if (valueHasNoError) {
							valueHasNoError = false;
						}
						errorList.add(errorMessage);
					}
				}
				
				if (valueHasNoError) {
					pojoList.add(popluatePojo(clazz, oneRowCells, fieldsMap));
				}
				lineNum++  ;
			}
		} catch (FileNotFoundException e) {
			List<String> errorList1 = new ArrayList<String>();
			errorList1.add("无法在目录【"+csvFilePath+"】下找到指定文件");
			throw new ExcelException(errorList1 );
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
		
		if (!errorList.isEmpty()) {
			throw new ExcelException(errorList);
		}
		return pojoList;
	}
	
	private <T> T popluatePojo(Class<T> clazz, String[] oneRowCells, Map<Integer, Field> fieldsMap) {
		T entity = null;
		try {
			entity = (entity == null ? clazz.newInstance() : entity);// 如果不存在实例则新建.
			for (int colNum = 0 ; colNum < fieldsMap.entrySet().size() ; colNum ++) {
				Field field = fieldsMap.get(colNum);// 从map中得到对应列的field.
				String c = oneRowCells[colNum];
				// 取得类型,并根据对象类型设置值.
				System.out.println(field);
				Class<?> fieldType = field.getType();
				if ((Integer.TYPE == fieldType)
						|| (Integer.class == fieldType)) {
					field.set(entity, Integer.parseInt(c));
				} else if (String.class == fieldType) {
					field.set(entity, String.valueOf(c));
				} else if ((Long.TYPE == fieldType)
						|| (Long.class == fieldType)) {
					field.set(entity, Long.valueOf(c));
				} else if ((Float.TYPE == fieldType)
						|| (Float.class == fieldType)) {
					field.set(entity, Float.valueOf(c));
				} else if ((Short.TYPE == fieldType)
						|| (Short.class == fieldType)) {
					field.set(entity, Short.valueOf(c));
				} else if ((Double.TYPE == fieldType)
						|| (Double.class == fieldType)) {
					field.set(entity, Double.valueOf(c));
				} else if (Character.TYPE == fieldType) {
					if ((c != null) && (c.length() > 0)) {
						field.set(entity, Character.valueOf(c.charAt(0)));
					}
				}
			}
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return entity;
	}

	private void validateSheetHeader(String[] headRowCells,
			List<SheetHeader> sheetHeaders) throws ExcelException {
		
		List<String> expectedHeaderNames = new ArrayList<String>();
		for (SheetHeader sh : sheetHeaders) {
			expectedHeaderNames .add(sh.getHeaderName());
		}
		
		List<String> errorList = new ArrayList<String>();
		//表头个数一致性校验
		if (headRowCells.length != expectedHeaderNames.size()) {
			StringBuilder sb = new StringBuilder("表头名称错误:");
			sb.append("文件中的表头为");
			sb.append(Arrays.deepToString(headRowCells));
			sb.append(",");
			sb.append("要求的表头为");
			sb.append(expectedHeaderNames);
			errorList .add(sb.toString());
			throw new ExcelException(errorList);
		}
		
		//表头对应一致性校验
		for (int i = 0 ; i < headRowCells.length ; i ++) {
			String inputHeaderName = headRowCells[i];
			String expectedHeaderName = expectedHeaderNames.get(i);
			if (!inputHeaderName.equals(expectedHeaderName)) {
				errorList.add("第1行" +(i+1)+"列的标题应该是【"+expectedHeaderName+"】");
			}
		}
		
		if (!errorList.isEmpty()) {
			throw new ExcelException(errorList);
		}
	}

	private static void validateFileName(FileNameValidator fileNameValidator,
			String fileName) throws ExcelException {
		ArrayList<String> errorList = new ArrayList<String>();
		String errorMessage = null;
		if (fileNameValidator != null && (errorMessage = fileNameValidator.fileNameValidate(fileName)) != null) {
			errorList.add(errorMessage);
			throw new ExcelException(errorList);
		}
	}
	
	/**
	 * 根据类的属性上面的注解来找到excel中每一列对应的属性
	 * @Title: matchEexcelColToField
	 * @Author: 崔勇	
	 * @Date Apr 6, 2014 10:32:11 AM
	 * @param clazz
	 * @return 键为列索引，值为Field的Map
	 */
	private <T> Map<Integer, Field> matchEexcelColToField(Class<T> clazz) {
		Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>();// 定义一个map用于存放列的序号和field.
		Field[] allFields = clazz.getDeclaredFields();// 得到类的所有field.
		for (Field field : allFields) {
			// 将有注解的field存放到map中.
			if (field.isAnnotationPresent(ExcelField.class)) {
				ExcelField attr = field
						.getAnnotation(ExcelField.class);
				int col = execelFormatColumnIndexToNumber(attr.column());// 获得列号
				// System.out.println(col + "====" + field.getName());
				field.setAccessible(true);// 设置类的私有字段属性可访问.
				fieldsMap.put(col, field);
			}
		}
		return fieldsMap;
	}
	
	/**
	 * 将EXCEL中A,B,C,D,E列映射成0,1,2,3
	 * @Title: execelFormatColumnIndexToNumber
	 * @Author: 崔勇	
	 * @Date Apr 6, 2014 11:54:24 AM
	 * @param col
	 * @return
	 */
	private int execelFormatColumnIndexToNumber(String col) {
		col = col.toUpperCase();
		// 从-1开始计算,字母重1开始运算。这种总数下来算数正好相同。
		int count = -1;
		char[] cs = col.toCharArray();
		for (int i = 0; i < cs.length; i++) {
			count += (cs[i] - 64) * Math.pow(26, cs.length - 1 - i);
		}
		return count;
	}

}
